﻿#include <utl/logger.h>
#include <system/window_system.h>
#include <gfx/vk/core.h>
#include <res/static_resource.h>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <gfx/vk/helper.h>
#include <gfx/vk/command.h>
#include <gui/VkGui.h>

/*
	utl::filebytes vert;
	utl::filebytes frag;

	primal::res::get_shader_byte(vert, primal::res::BaseVert);
	primal::res::get_shader_byte(frag, primal::res::BaseFrag);

	{
		utl::vector<VkPresentModeKHR> present_modes{};
		u32 pcount{};
		vkGetPhysicalDeviceSurfacePresentModesKHR(primal::gfx::phy(), g_swapchain.surface, &pcount, nullptr);
		present_modes.resize(pcount);
		vkGetPhysicalDeviceSurfacePresentModesKHR(primal::gfx::phy(), g_swapchain.surface, &pcount, present_modes.data());
	}
*/

GLFWwindow* g_window;


constexpr u32 SwapChainCount{ 4u };
constexpr u32 SwapChainCount_1{ SwapChainCount - 1u };

struct Swapchain
{
	VkSurfaceKHR surface;
	VkSwapchainKHR swapchain;
	VkRenderPass render_pass;
	VkShaderModule vs;
	VkShaderModule ps;
	VkPipelineLayout pipe_layout;
	VkPipeline pipeline;

	VkImage images[SwapChainCount];
	VkImageView img_views[SwapChainCount];
	VkFramebuffer frame_buffers[SwapChainCount];
	VkSemaphore frame_semaphores[SwapChainCount];
	u32 swap_index;
	VkDescriptorPool desc_pool;
}g_render_data{};

void create_shader(VkShaderModule* handle, primal::res::SHADER_BYTE_TYPE type)
{
	primal::vkx::shader_create_info sci{};
	utl::filebytes data{};
	primal::res::get_shader_byte(data, type);
	VKASSERT(primal::gfx::dev().CreateShaderModule(sci.init(data.size(), data.data()), handle));
}

void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
{
	auto& vk_device{ primal::gfx::dev() };
	
	VkCommandBufferBeginInfo beginInfo{};
	beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

	vk_device.vkBeginCommandBuffer(commandBuffer, &beginInfo);

	VkRenderPassBeginInfo renderPassInfo{};
	renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
	renderPassInfo.renderPass = g_render_data.render_pass;
	renderPassInfo.framebuffer = g_render_data.frame_buffers[imageIndex];
	renderPassInfo.renderArea.offset = { 0, 0 };
	renderPassInfo.renderArea.extent.width = 800;
	renderPassInfo.renderArea.extent.height = 800;

	VkClearValue clearColor = { {{1.0f, 0.0f, 0.0f, 1.0f}} };
	renderPassInfo.clearValueCount = 1;
	renderPassInfo.pClearValues = &clearColor;

	vk_device.vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);

	vk_device.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render_data.pipeline);

	VkViewport viewport{};
	viewport.x = 0.0f;
	viewport.y = 0.0f;
	viewport.width = (float)800;
	viewport.height = (float)800;
	viewport.minDepth = 0.0f;
	viewport.maxDepth = 1.0f;
	vk_device.vkCmdSetViewport(commandBuffer, 0, 1, &viewport);

	VkRect2D scissor{};
	scissor.offset = { 0, 0 };
	scissor.extent.width = 800;
	scissor.extent.height = 800;
	vk_device.vkCmdSetScissor(commandBuffer, 0, 1, &scissor);

	vk_device.vkCmdDraw(commandBuffer, 3, 1, 0, 0);	
}

void createRenderPass()
{
	auto& vk_device{ primal::gfx::dev() };

	g_render_data.render_pass = primal::gfx::get_pass(primal::gfx::RENDERE_PASS_TYPE::UI);

	create_shader(&g_render_data.vs, primal::res::BaseVert);
	create_shader(&g_render_data.ps, primal::res::BaseFrag);

	primal::vkx::shader_states shader_state{};
	shader_state.add_vs(g_render_data.vs);
	shader_state.add_ps(g_render_data.ps);

	primal::vkx::pipeline_layout_create_info<0> pip_layout_info{};
//	vk_device.CreatePipelineLayout(pip_layout_info.init(), &g_render_data.pipe_layout);
}

void createGraphicsPipeline()
{

	VkShaderModule vertShaderModule{}, fragShaderModule{};
	create_shader(&vertShaderModule, primal::res::BaseVert);
	create_shader(&fragShaderModule, primal::res::BaseFrag);

	primal::vkx::shader_states shader_state{};
	shader_state.add_vs(vertShaderModule);
	shader_state.add_ps(fragShaderModule);

	VkPipelineRasterizationStateCreateInfo rasterizer{};
	rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
	rasterizer.depthClampEnable = VK_FALSE;
	rasterizer.rasterizerDiscardEnable = VK_FALSE;
	rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
	rasterizer.lineWidth = 1.0f;
	rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
	rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
	rasterizer.depthBiasEnable = VK_FALSE;

	VkPipelineMultisampleStateCreateInfo multisampling{};
	multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
	multisampling.sampleShadingEnable = VK_FALSE;
	multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;

	VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
	pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
	pipelineLayoutInfo.setLayoutCount = 0;
	pipelineLayoutInfo.pushConstantRangeCount = 0;
	//pipelineLayoutInfo.pSetLayouts;
	VkDescriptorSetLayoutCreateInfo set_layout_create_info{};
	//set_layout_create_info.pBindings;
	VkDescriptorSetLayoutBinding binding{};
	
	VkDescriptorSetLayout layouts{};
	auto& vk_device{ primal::gfx::dev() };

	vk_device.CreateDescriptorSetLayout(&set_layout_create_info, &layouts);
	VkDescriptorSetAllocateInfo all_info{};
	
	//vk_device.vkCmdBindVertexBuffers()
	//vk_device.AllocateDescriptorSets();

	VKASSERT(vk_device.CreatePipelineLayout(&pipelineLayoutInfo, &g_render_data.pipe_layout));

	VkGraphicsPipelineCreateInfo pipelineInfo{};
	pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
	pipelineInfo.stageCount = shader_state.size();
	pipelineInfo.pStages = shader_state.get();
	pipelineInfo.pVertexInputState = &primal::vkx::def.vertex_input_null;
	pipelineInfo.pInputAssemblyState = &primal::vkx::def.input_assembly;
	pipelineInfo.pViewportState = &primal::vkx::def.viewport_null;
	pipelineInfo.pRasterizationState = &primal::vkx::rasterization_state_create_infos.rasterization_state;
	pipelineInfo.pMultisampleState = &primal::vkx::multisample_state_create_infos.multisample_state;
	pipelineInfo.pColorBlendState = &primal::vkx::color_blend_state_create_infos.ui;
	pipelineInfo.pDynamicState = &primal::vkx::dynamic_state_create_infos.vieweport;
	pipelineInfo.layout = g_render_data.pipe_layout;
	pipelineInfo.renderPass = g_render_data.render_pass;
	pipelineInfo.subpass = 0;
	pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;

	VKASSERT(vk_device.CreateGraphicsPipelines(nullptr, 1u, &pipelineInfo, &g_render_data.pipeline));
}

class frame_show : primal::GuiWindowBase
{
public:
	frame_show() :GuiWindowBase{ u8"测试" }
	{

	}
private:
	void content(const primal::FontList& icon_ft) override
	{
		ImGui::Text("%.3f", ImGui::GetIO().Framerate);
	}
};

int main(int argc, char** argv)
{
	frame_show fw{};
	auto& vk_device{ primal::gfx::dev() };

	primal::gfx::initialize();
	auto window = primal::GetWS().CreateWnd();
	g_window = window.get();

	primal::gui::initialize(g_window);
	primal::gui::set_theme_blender();

	VKASSERT(glfwCreateWindowSurface(primal::gfx::vk_instance(), g_window, primal::gfx::alloc_cbk(), &g_render_data.surface));

	primal::gfx::GetPhysicalDeviceSurfaceFormats(g_render_data.surface);

	primal::vkx::swapchain_create_info swap_info{};

	VKASSERT(vk_device.CreateSwapchainKHR(swap_info.init(g_render_data.surface, SwapChainCount, 800u, 800u), &g_render_data.swapchain));

	u32 swapchain_cout{ SwapChainCount };
	VKASSERT(vk_device.GetSwapchainImagesKHR(g_render_data.swapchain, &swapchain_cout, g_render_data.images));

	
	createRenderPass();
	createGraphicsPipeline();


	for(u32 i{}; i < SwapChainCount; ++i)
	{
		primal::vkx::image_view_create_info img_view_info{};
		VKASSERT(vk_device.CreateImageView(img_view_info.init(g_render_data.images[i]), &g_render_data.img_views[i]));
		primal::vkx::frame_buffer_create_info frame_info{};
		VKASSERT(vk_device.CreateFramebuffer(frame_info.init(g_render_data.render_pass, 1u, &g_render_data.img_views[i], 800u, 800u), &g_render_data.frame_buffers[i]));
		VKASSERT(vk_device.CreateSemaphore(&g_render_data.frame_semaphores[i]));
	}


	primal::gfx::CommandBase<4ull> cmd_base{};

	while(!glfwWindowShouldClose(g_window))
	{
		glfwPollEvents();

		uint32_t imageIndex;

		auto& imageAvailableSemaphore{ g_render_data.frame_semaphores[g_render_data.swap_index] };


		auto cmd_buf = cmd_base.begin();

		VKASSERT(vk_device.AcquireNextImageKHR(g_render_data.swapchain, UINT64_MAX, imageAvailableSemaphore, nullptr, &imageIndex));

		recordCommandBuffer(cmd_buf, g_render_data.swap_index);

		primal::gui::set_render_comamnd(cmd_buf);
		primal::gui::render();

		vk_device.vkCmdEndRenderPass(cmd_buf);

		auto waitable = cmd_base.end(&imageAvailableSemaphore, 1u);

		VkPresentInfoKHR presentInfo{};
		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

		presentInfo.waitSemaphoreCount = 1;
		presentInfo.pWaitSemaphores = &waitable;

		VkSwapchainKHR swapChains[] = { g_render_data.swapchain };
		presentInfo.swapchainCount = 1;
		presentInfo.pSwapchains = swapChains;

		presentInfo.pImageIndices = &g_render_data.swap_index;

		VKASSERT(vk_device.vkQueuePresentKHR(primal::gfx::graphics_queue(), &presentInfo));

		++g_render_data.swap_index;
		g_render_data.swap_index &= SwapChainCount_1;
	}

	return 0;
}